/*
*  Copyright 2019-2025 Zheng Jie
*
*  Licensed under the Apache License, Version 2.0 (the "License");
*  you may not use this file except in compliance with the License.
*  You may obtain a copy of the License at
*
*  http://www.apache.org/licenses/LICENSE-2.0
*
*  Unless required by applicable law or agreed to in writing, software
*  distributed under the License is distributed on an "AS IS" BASIS,
*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*  See the License for the specific language governing permissions and
*  limitations under the License.
*/
package me.zhengjie.gas.service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import me.zhengjie.dto.DeptDto;
import me.zhengjie.gas.dto.DeptSettingsDto;
import me.zhengjie.gas.dto.DtuDevice;
import me.zhengjie.gas.dto.TransactionDataDto;
import me.zhengjie.gas.mqtt.MqttTemplate;
import me.zhengjie.gas.proto.TankerComm;
import me.zhengjie.gas.proto.recv.E3Comm;
import me.zhengjie.gas.proto.send.B2Comm;
import me.zhengjie.gas.repositories.DeptSettingsRepository;
import me.zhengjie.gas.repositories.DtuDeviceRepository;
import me.zhengjie.gas.repositories.TransactionRepository;
import me.zhengjie.gas.service.mapstruct.DtuDeviceMapper;
import me.zhengjie.service.DeptService;
import me.zhengjie.utils.ValidationUtil;
import me.zhengjie.utils.FileUtil;
import lombok.RequiredArgsConstructor;
import me.zhengjie.gas.service.dto.DtuDeviceDto;
import me.zhengjie.gas.service.dto.DtuDeviceQueryCriteria;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import me.zhengjie.utils.PageUtil;
import me.zhengjie.utils.QueryHelp;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.io.IOException;
import javax.servlet.http.HttpServletResponse;

import me.zhengjie.utils.PageResult;

/**
* @website https://eladmin.vip
* @description 服务实现
* @author Joe Tan
* @date 2025-08-14
**/
@Service
@RequiredArgsConstructor
public class DtuDeviceService {

    @Autowired
    private DtuDeviceRepository dtuDeviceRepository;

    @Autowired
    private DtuDeviceMapper dtuDeviceMapper;

    @Autowired
    private DeptService deptService;

    @Autowired
    private TransactionRepository transactionRepository;

    @Autowired
    private TransactionService transactionService;

    @Autowired
    private DeptSettingsRepository deptSettingsRepository;


    public PageResult<DtuDeviceDto> queryAll(DtuDeviceQueryCriteria criteria, Pageable pageable){
        Page<DtuDevice> page = dtuDeviceRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),pageable);
        return PageUtil.toPage(page.map(dtuDeviceMapper::toDto));
    }

    public List<DtuDeviceDto> queryAll(DtuDeviceQueryCriteria criteria){
        return dtuDeviceMapper.toDto(dtuDeviceRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder)));
    }

    @Transactional
    public DtuDeviceDto findById(Integer id) {
        DtuDevice dtuDevice = dtuDeviceRepository.findById(id).orElseGet(DtuDevice::new);
        ValidationUtil.isNull(dtuDevice.getId(),"DtuDevice","id",id);
        return dtuDeviceMapper.toDto(dtuDevice);
    }

    @Transactional(rollbackFor = Exception.class)
    public void create(DtuDevice resources) {
        dtuDeviceRepository.save(resources);
    }

    @Transactional(rollbackFor = Exception.class)
    public void update(DtuDevice resources) {
        DtuDevice dtuDevice = dtuDeviceRepository.findById(resources.getId()).orElseGet(DtuDevice::new);
        ValidationUtil.isNull( dtuDevice.getId(),"DtuDevice","id",resources.getId());
        dtuDevice.copy(resources);
        dtuDeviceRepository.save(dtuDevice);
    }

    public void deleteAll(Integer[] ids) {
        for (Integer id : ids) {
            dtuDeviceRepository.deleteById(id);
        }
    }

    public void download(List<DtuDeviceDto> all, HttpServletResponse response) throws IOException {
        List<Map<String, Object>> list = new ArrayList<>();
        for (DtuDeviceDto dtuDevice : all) {
            Map<String,Object> map = new LinkedHashMap<>();
            map.put("dept_id", dtuDevice.getDeptId());
            map.put("device_name", dtuDevice.getDeviceName());
            map.put("device_mac", dtuDevice.getDeviceMac());
            map.put("device_imei", dtuDevice.getDeviceImei());
            map.put("is_active", dtuDevice.getIsActive());
            map.put("remark", dtuDevice.getRemark());
            map.put("created_ts", dtuDevice.getCreatedTs());
            map.put("created_by", dtuDevice.getCreatedBy());
            map.put("last_updated_ts", dtuDevice.getLastUpdatedTs());
            map.put("last_updated_by", dtuDevice.getLastUpdatedBy());
            list.add(map);
        }
        FileUtil.downloadExcel(list, response);
    }

    public byte[] handleBytes(String deviceId, byte[] payload) {
        TankerComm comm = TankerComm.parse(payload);
        if (comm == null || !(comm instanceof E3Comm)) {
            return null;
        }

        E3Comm e3Comm = (E3Comm) comm;
        DtuDevice device = dtuDeviceRepository.findByDeviceMac(deviceId);
        if (device == null) {
            return null;
        }

        DeptDto deptDto = deptService.findById(device.getDeptId());
        if (deptDto == null) {
            return null;
        }

        DeptSettingsDto settingsDto = deptSettingsRepository.findByDeptId(device.getDeptId());
        int volumeDecimal = 2;
        int amountDecimal = 2;
        int priceDecimal = 2;
        if (settingsDto != null) {
            volumeDecimal = settingsDto.getVolumeDecimal();
            amountDecimal = settingsDto.getAmountDecimal();
            priceDecimal = settingsDto.getPriceDecimal();
        }

        TransactionDataDto transactionDataDto = new TransactionDataDto();
        transactionDataDto.setDeptId(device.getDeptId());
        transactionDataDto.setTransactionId(e3Comm.getTransactionId());
        transactionDataDto.setLineId((int) e3Comm.getNO());
        transactionDataDto.setOilId(e3Comm.getProductNumber());
        transactionDataDto.setOilName("" + e3Comm.getProductNumber());
        transactionDataDto.setVolume(convertValue(e3Comm.getVolume(), volumeDecimal));
        transactionDataDto.setPrice(convertValue(e3Comm.getUnitPrice(), priceDecimal));
        transactionDataDto.setAmount(convertValue(e3Comm.getAmount(), amountDecimal));
        transactionDataDto.setTotalLitres(convertValue(e3Comm.getTotalLitres(), volumeDecimal));
        transactionDataDto.setEmployeeId(Integer.parseInt(e3Comm.getEmployeeId()));
        transactionDataDto.setTransactionType(e3Comm.getTransactionType());
        transactionDataDto.setTransactionTime(LocalDateTime.ofInstant(e3Comm.getTransactionTime().toInstant(), ZoneId.systemDefault()));
        transactionDataDto.setLogicCardId(e3Comm.getLogicCardId());
        transactionDataDto.setPhysicalCardId(e3Comm.getPhysicalCardId());
        transactionDataDto.setCardBalance(convertValue(e3Comm.getCardBalance(), amountDecimal));
        transactionDataDto.setCtc(e3Comm.getCtc() + "");
        transactionDataDto.setListedAmount(convertValue(e3Comm.getListedAmount(), amountDecimal));
        transactionDataDto.setListedPrice(convertValue(e3Comm.getListedPrice(), priceDecimal));
        transactionDataDto.setCreatedTs(LocalDateTime.now());
        transactionDataDto.setUuidStr(UUID.randomUUID());
        transactionDataDto.setTerminalUuid(null);
        transactionDataDto.setDeviceId(deviceId);

        TransactionDataDto last = transactionRepository.findLastByDeptIdAndLineId(device.getDeptId(), transactionDataDto.getLineId(), transactionDataDto.getDeviceId());
        boolean isRepeat = false;
        if (last != null) {
            if (last.getVolume() == transactionDataDto.getVolume()
                    && last.getAmount() == transactionDataDto.getAmount()
                    && last.getPrice() == transactionDataDto.getPrice()
                    && last.getTotalLitres() == transactionDataDto.getTotalLitres()) {
                isRepeat = true;
            }

            double totalLitres = last.getTotalLitres() + transactionDataDto.getVolume();
            if (!doubleComparison(totalLitres, transactionDataDto.getTotalLitres())) {
                System.out.println("totalLitres: " + totalLitres +  "  newTotalLitres: " + transactionDataDto.getTotalLitres() + "  || false");
                transactionDataDto.setWrongCode(1);
            }
        }

        if (!isRepeat) {
            transactionRepository.save(transactionDataDto);
            transactionService.handleTransaction(transactionDataDto);

            ObjectMapper objectMapper = new ObjectMapper();
            objectMapper.registerModule(new JavaTimeModule());
            try {
                String jsonText = objectMapper.writeValueAsString(transactionDataDto);
                MqttTemplate.publish("/GAS/" + deptDto.getId() + "/transaction", jsonText);
            } catch (JsonProcessingException e) {
                throw new RuntimeException(e);
            }
        }

        B2Comm reply = new B2Comm();
        reply.setNO(e3Comm.getNO());
        reply.setState((byte) 1);
        return reply.encode();
    }

    private double convertValue(String value, int unit) {
        return Integer.parseInt(value) / Math.pow(10, unit);
    }

    public static boolean doubleComparison(Double t1, Double t2) {
        // 四舍五入保留两位小数
        double roundedA = Math.round(t1 * 10000.0) / 10000.0;
        double roundedB = Math.round(t2 * 10000.0) / 10000.0;

        // 比较两个 double
        return (roundedA == roundedB);
    }
}